/* Copyright 2013 (C) FatalEror / Dean Bahari
 * 
 * jroCK is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * See LICENSE file in main folder for more information.
 */

package jrock.filesystem;

import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.util.glu.MipMap.gluBuild2DMipmaps;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.IntBuffer;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.lwjgl.BufferUtils;
import org.lwjgl.util.vector.Vector2f;

/**
 * @author FatalEror
 */
public class BMP {

    private BMPHeader  header     = new BMPHeader();
    private InfoHeader infoHeader = new InfoHeader();
    private int        texIndex   = 0;
    
    private class BMPHeader {
        private short type;
        private int   size;
        private short reserved1;
        private short reserved2;
        private int   offBits;
    }
    
    private class InfoHeader {
        private int   size;
        private int   width;
        private int   height;
        private short planes;
        private short bitCount;
        private int   compression;
        private int   sizeImage;
        private int   xPelsPerMeter;
        private int   yPelsPerMeter;
        private int   colorUsed;
        private int   colorImportant;
    }
    
    private class RGBQuad {
        private byte blue = 0;
        private byte green = 0;
        private byte red = 0;
        private byte alpha = 0;
    }
    
    @SuppressWarnings("empty-statement")
    public boolean read(GRF grf, String fileName) {
        try {
            ByteBuffer bb = grf.getData("data\\" + fileName);
            bb.order(ByteOrder.LITTLE_ENDIAN);
            
            // BMP Header
            header.type      = bb.getShort();
            header.size      = bb.getInt();
            header.reserved1 = bb.getShort();
            header.reserved2 = bb.getShort();
            header.offBits   = bb.getInt();
            
            // BMP Info Header
            infoHeader.size           = bb.getInt();
            infoHeader.width          = bb.getInt();
            infoHeader.height         = bb.getInt();
            infoHeader.planes         = bb.getShort();
            infoHeader.bitCount       = bb.getShort();
            infoHeader.compression    = bb.getInt();
            infoHeader.sizeImage      = bb.getInt();
            infoHeader.xPelsPerMeter  = bb.getInt();
            infoHeader.yPelsPerMeter  = bb.getInt();
            infoHeader.colorUsed      = bb.getInt();
            infoHeader.colorImportant = bb.getInt();

            int size = infoHeader.width * infoHeader.height * infoHeader.bitCount / 8;
            if (infoHeader.colorUsed == 0) {
                infoHeader.colorUsed = 1 << infoHeader.bitCount;
            }

            ByteBuffer pixelData = BufferUtils.createByteBuffer(size * 4);
            if (infoHeader.bitCount == 8) {
                RGBQuad[] palette = new RGBQuad[infoHeader.colorUsed];
                for (int i = 0; i < infoHeader.colorUsed; i++) {
                    palette[i] = new RGBQuad();
                    palette[i].blue  = bb.get();
                    palette[i].green = bb.get();
                    palette[i].red   = bb.get();
                    palette[i].alpha = bb.get();

                    // Purple is transparent
                    if (palette[i].red == -1 && palette[i].green == 0 && palette[i].blue == -1) {
                        palette[i].blue  = -1;
                        palette[i].green = -1;
                        palette[i].red   = -1;
                        palette[i].alpha = 0;
                    } else {
                        palette[i].alpha = -1;
                    }
                }

                RGBQuad curPalette;
                int curOffset;
                int lenPadding = infoHeader.width % 4;
                for (int y = infoHeader.height - 1; y >= 0; y--) {
                    for (int x = 0; x < infoHeader.width; x++) {
                        curOffset  = (y * infoHeader.width + x) * 4;
                        curPalette = palette[(int)bb.get() & 0xFF];
                        pixelData.put(curOffset,     curPalette.red);
                        pixelData.put(curOffset + 1, curPalette.green);
                        pixelData.put(curOffset + 2, curPalette.blue);
                        pixelData.put(curOffset + 3, curPalette.alpha);
                    }
                    bb.position(bb.position() + lenPadding);
                }
            } else {
                int curOffset;
                int lenPadding = infoHeader.width % 4;
                for (int y = infoHeader.height - 1; y >= 0; y--) {
                    for (int x = 0; x < infoHeader.width; x++) {
                        curOffset  = (y * infoHeader.width + x) * 4;
                        pixelData.put(curOffset,     bb.get());
                        pixelData.put(curOffset + 1, bb.get());
                        pixelData.put(curOffset + 2, bb.get());
                        // Purple is transparent
                        if (pixelData.get(curOffset) == -1 && pixelData.get(curOffset + 1) == 0 && pixelData.get(curOffset + 2) == -1) {
                            pixelData.put(curOffset,     (byte)-1);
                            pixelData.put(curOffset + 1, (byte)-1);
                            pixelData.put(curOffset + 2, (byte)-1);
                            pixelData.put(curOffset + 3, (byte)0);
                        } else {
                            pixelData.put(curOffset + 3, (byte)-1);
                        }
                    }
                    bb.position(bb.position() + lenPadding);
                }
            }
            pixelData.position(size * 4);
            pixelData.flip();
            
            IntBuffer ib = BufferUtils.createIntBuffer(1);
            glGenTextures(ib);
            glBindTexture(GL_TEXTURE_2D, ib.get(0));
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
            gluBuild2DMipmaps(GL_TEXTURE_2D, 4, infoHeader.width, infoHeader.height, GL_RGBA, GL_UNSIGNED_BYTE, pixelData);
            
            texIndex = ib.get(0);
        } catch (Exception ex) {
            Logger.getLogger(BMP.class.getName()).log(Level.SEVERE, null, ex);
            return false;
        }
        
        return true;
    }

    public void bind() {
        glBindTexture(GL_TEXTURE_2D, texIndex);
    }
    
    public void destroy() {
        glDeleteTextures(texIndex);
    }
}
